#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>

#include "Service.h"
#include "TypesCompatibility.h"

//////////////////////////////////////////////////
// IMPLEMENTACION DE LA HEBRA PARA CADA CLIENTE //
//////////////////////////////////////////////////

ClientThread::ClientThread(int sfd, Service* s) : Thread()
{
	sockfd = sfd;
	service = s;
}

void ClientThread::run()
{
	pthread_detach(pthread_self());
	
	int n;
	char buffer[256];
	std::string msg;
	
	while(1)
	{
		msg = "";
		while ((n = read(sockfd,buffer,255)) > 0)
		{
			msg = msg + buffer;
			
			if (buffer[n-1] == '\0') //fin de cadena
				break;
		}
		
		if (n <= 0) //el cliente ha sido desconectado
			break;
		
		//reenvio del mensaje a todos los clientes
		service->broadCast(msg);
	}
	
	service->removeClient(sockfd);
}

//////////////////////////////////////////////////
//         IMPLEMENTACION DEL SERVICIO          //
//////////////////////////////////////////////////

Service::Service(int p, bool trc) : Thread()
{
	trace = trc;
	port = p;
	pthread_mutex_init(&mutex, NULL);
}

Service::~Service()
{
	std::list<int>::iterator it;
	for (it=clients.begin(); it!=clients.end(); ++it)
		close(*it);
	
	close(sockfd);
	pthread_mutex_destroy(&mutex);
}

void Service::waitToEnd()
{
	this->join();
}

void Service::broadCast(const std::string& msg)
{
	pthread_mutex_lock(&mutex);
	
	//recorrer la lista de clientes conectados
	std::list<int>::iterator it;
	for (it=clients.begin(); it!=clients.end(); ++it)
	{
		//enviar el mensaje
		write(*it, msg.c_str(), msg.length()+1);
	}
	
	pthread_mutex_unlock(&mutex);
}

void Service::removeClient(int socket)
{
	std::list<int>::iterator it;
	std::list<struct sockaddr_in>::iterator it_data;
	
	pthread_mutex_lock(&mutex);
	
	it_data = clients_data.begin();
	for (it = clients.begin(); it!=clients.end(); ++it)
	{
		if (*it == socket)
		{
			clients.erase(it);
			break;
		}
		
		++it_data;
	}
	
	close(socket);
	
	if (trace) std::cout << "Cliente desconectado [" << inet_ntoa((*it_data).sin_addr) << "]" << std::endl;
	
	clients_data.erase(it_data);
	
	pthread_mutex_unlock(&mutex);
}

void Service::run()
{
	int newsockfd;
	socklen_t clilen;
	ClientThread* ct; //hebras para cada cliente
	
	char buffer[256];
	struct sockaddr_in serv_addr, cli_addr;
	
	sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		std::cerr << "Error abriendo el socket" << std::endl;
		exit(1);
	}

	bzero((char *) &serv_addr, sizeof(serv_addr));
	
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(port);
	if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
	{
		std::cerr << "Error en el proceso de binding" << std::endl;
		exit(1);
	}
	
	listen(sockfd,5);
	
	if (trace) 
		std::cout << "Servicio iniciado correctamente [" << inet_ntoa(serv_addr.sin_addr) << ":" << port << "]" << std::endl;
	
	while(1)
	{
		newsockfd = accept(sockfd, 
					   (struct sockaddr*)&cli_addr, 
					   &clilen);
					
		if (newsockfd >= 0)
		{
			pthread_mutex_lock(&mutex);
			
			clients_data.push_back(cli_addr);
			clients.push_back(newsockfd);
			
			pthread_mutex_unlock(&mutex);
			
			ct = new ClientThread(newsockfd, this);
			ct->start();
			
			if (trace) std::cout << "Nuevo cliente conectado [" << inet_ntoa(cli_addr.sin_addr) << "]" << std::endl;
		}
	}
}
